/****************************************************************************
 * infineon/chips/tc3xx/tc3xx_main.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

#include <tricore_internal.h>
#include <nuttx/init.h>

#include "Ifx_Types.h"
#include "Ifx_Cfg.h"
#include "IfxCpu.h"
#include "Ifx_Ssw_Infra.h"
#include "Ifx_Ssw_Compilers.h"
#include "tricore.h"

IFX_SSW_COMMON_LINKER_SYMBOLS();
#define IFX_CPU_START_CORE(cpu) Ifx_Ssw_startCore(&MODULE_CPU##cpu, (unsigned int)__START(cpu))

static void Ifx_start_cores(void)
{
#if (CONFIG_BMP_NCPUS > 1)
	IFX_CPU_START_CORE(1);
#endif
#if (CONFIG_BMP_NCPUS > 2)
	IFX_CPU_START_CORE(2);
#endif
#if (CONFIG_BMP_NCPUS > 3)
	IFX_CPU_START_CORE(3);
#endif
#if (CONFIG_BMP_NCPUS > 4)
	IFX_CPU_START_CORE(4);
#endif
#if (CONFIG_BMP_NCPUS > 5)
	IFX_CPU_START_CORE(5);
#endif
}
#if defined(__TASKING__)
extern unsigned long _lc_gb_core0_bmp_data[];
extern unsigned long _lc_ge_core0_bmp_data[];
extern unsigned long _lc_gb_core1_bmp_data[];
extern unsigned long _lc_gb_core2_bmp_data[];
extern unsigned long _lc_gb_core3_bmp_data[];
extern unsigned long _lc_gb_core4_bmp_data[];
extern unsigned long _lc_gb_core5_bmp_data[];

const unsigned long g_per_cpu_ram_base_addr[6] = {
	(unsigned long)_lc_gb_core0_bmp_data, (unsigned long)_lc_gb_core1_bmp_data,
	(unsigned long)_lc_gb_core2_bmp_data, (unsigned long)_lc_gb_core3_bmp_data,
	(unsigned long)_lc_gb_core4_bmp_data, (unsigned long)_lc_gb_core5_bmp_data,
};
#elif defined(__GNUC__) && defined(__TRICORE__)
unsigned long g_cpu_data_size;
unsigned long g_percpu_base;

#define DECLARE_LINKER_SYMBOLS(cpu)                                            \
	extern unsigned int __PERCPU##cpu##_START[];                           \
	extern unsigned int __PERCPU##cpu##_END[]

DECLARE_LINKER_SYMBOLS(0);
DECLARE_LINKER_SYMBOLS(1);
DECLARE_LINKER_SYMBOLS(2);
DECLARE_LINKER_SYMBOLS(3);
DECLARE_LINKER_SYMBOLS(4);
DECLARE_LINKER_SYMBOLS(5);

#define __PERCPU(cpu) __PERCPU##cpu##_START
#define __PERCPU_END(cpu) __PERCPU##cpu##_END

const unsigned long g_per_cpu_ram_base_addr[] = {
	(unsigned long)(__PERCPU(0)), (unsigned long)(__PERCPU(1)),
	(unsigned long)(__PERCPU(2)), (unsigned long)(__PERCPU(3)),
	(unsigned long)(__PERCPU(4)), (unsigned long)(__PERCPU(5)),
};
#endif /* __TASKING__ */

static void core_main(void)
{
	IfxCpu_syncEvent g_sync_event = 0;
#if defined(__TASKING__)
	uint32_t percpu_data_size = (uint32_t)_lc_ge_core0_bmp_data - (uint32_t)_lc_gb_core0_bmp_data;
#endif /* __TASKING__ */

	/* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
	 * Enable the watchdogs and service them periodically if it is required
	 */
	IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
	if (IfxCpu_getCoreIndex() == 0) {
		IfxScuWdt_disableSafetyWatchdog(
			IfxScuWdt_getSafetyWatchdogPassword());
	}
	/* Wait for CPU sync event */
	IfxCpu_emitEvent(&g_sync_event);
	IfxCpu_waitEvent(&g_sync_event, 1);
	if (IfxCpu_getCoreIndex() == 0) {
		tricore_earlyserialinit();
#ifdef CONFIG_BMP
#if defined(__TASKING__)
		/* Copy data/bss to each of CPUs */
		for (int i = 1; i < CONFIG_BMP_NCPUS; i++) {
			memcpy((void *)(g_per_cpu_ram_base_addr[i]),
					(void *)(g_per_cpu_ram_base_addr[0]),
					percpu_data_size);
		}

#elif defined(__GNUC__) && defined(__TRICORE__)
		g_cpu_data_size = (unsigned long)__PERCPU_END(0) - (unsigned long)__PERCPU(0);
		g_percpu_base = (unsigned long)__PERCPU(0);
		for (int i = 1; i < CONFIG_BMP_NCPUS; i++) {
			memcpy((void *)(g_per_cpu_ram_base_addr[i]),
			       (void *)g_percpu_base, g_cpu_data_size);
		}
#endif /* __TASKING__ */
		Ifx_start_cores();
#endif /* CONFIG_BMP */
		nx_start();
	} else {
		if (IfxCpu_getCoreIndex() < CONFIG_BMP_NCPUS) {
			nx_start();
		}
	}
	/* This would be an appropriate place to put some MCU-specific logic to
	 * sleep in a reduced power mode until an interrupt occurs to save power
	 */
	while (1) {
		Ifx_Ssw_infiniteLoop();
	}
}

#define DEFINE_CPU_CORE_MAIN(cpu)                                              \
void core##cpu##_main(void)                                            \
{                                                                      \
	core_main();                                                   \
}

DEFINE_CPU_CORE_MAIN(0)
DEFINE_CPU_CORE_MAIN(1)
DEFINE_CPU_CORE_MAIN(2)
DEFINE_CPU_CORE_MAIN(3)
DEFINE_CPU_CORE_MAIN(4)
DEFINE_CPU_CORE_MAIN(5)
